/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <avr/io.h>
#include <avr/interrupt.h>
#include "common.h"
#include "delay_handler.h"

#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<PE)
#define DATA_OVERRUN (1<<DOR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)

#define RX_BUFFER_SIZE (256)

static char rx_buffer[RX_BUFFER_SIZE]; /* USART Receiver buffer */
static int rx_wr_index,rx_rd_index; /*Read and write pointers for usart buffer*/ 
// This flag is set on USART Receiver buffer overflow
static unsigned char rx_buffer_overflow;
static volatile int rxCounter; /* Number of unread bytes in rx_buffer*/

/////////////////////////////////////////////////////////////////////
// USART Receiver interrupt service routine
ISR(USART_RXC_vect)
//interrupt [USART_RXC] void usart_rx_isr(void)
{
  char status,data;
  status=UCSRA;
  data=UDR;
  if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) {
           
    // add data to the pointer and advance the write pointer
    rx_buffer[rx_wr_index] = data;
    if (++rx_wr_index == RX_BUFFER_SIZE)
      rx_wr_index = 0;

    if (++rxCounter == RX_BUFFER_SIZE) {
      rxCounter = 0;
      rx_buffer_overflow = 1;
    }
  }
}

char uartReceiveByte(int32_t timeout_ms)
{
  char retVal = RET_OK;

  //wait given timeout
  while (timeout_ms > 0) {
    asm volatile("cli");
    if(rxCounter != 0){
      break; //Break while loop. Data available on uart
    }
    asm volatile("sei");
    delay_ms(1);
    timeout_ms--;
  }
  asm volatile("sei");

  if (timeout_ms == 0) goto TIMEOUT;

  // get the data and advance the read pointer
  retVal = rx_buffer[rx_rd_index];
  if (++rx_rd_index == RX_BUFFER_SIZE)
    rx_rd_index = 0;

  asm volatile("cli");
  --rxCounter;
  asm volatile("sei");

  return retVal;

 TIMEOUT:
  return RET_TIMEOUT;
}

void uartSendChar(char c)
{
  loop_until_bit_is_set(UCSRA, UDRE);
  UDR = c;
}

void uartSendString(char * pStr, int16_t len)
{
  int16_t i;

  for(i=0; i<len; i++){
    uartSendChar(pStr[i]);
  }
}

int uartGetRxCounter(void)
{
    return rxCounter;
}

void uartResetRxCounter(void)
{
    rxCounter = 0;
    rx_wr_index = 0;
    rx_rd_index = 0;
}
